/* Visualizing a formula for an angle that spans a given cord length. Two
 * draggable circles towards the top of the screen control the circle radius
 * and cord length, the program finds an appropriate cord on the circle(red),
 * which is the same as the angle vector(green), then rotates a couple of
 * points based on angle representations derived from it: radians(yellow),
 * bisector(purple).
 */

#include "common/sketchbook.hpp"

constexpr float corner_radius = 14.f;
constexpr float2 half = float2::one(.5f);
const float tau = 2*std::acos(-1);


struct point
{
	enum
	{
		radius,
		cord_length,

		count
	};
};

std::array<float2, point::count> points;

float2* dragged_point = nullptr;

bool is_near(float2 corner, float2 position);

void start(Program& program)
{

	points[point::radius] = float2::one(20);
	points[point::cord_length] = float2::one(20);

	program.key_up = [&program](scancode code, keycode)
	{
		switch(code)
		{
			case scancode::leftbracket:
			case scancode::c:
				if(pressed(scancode::rctrl) || pressed(scancode::lctrl))
			case scancode::escape:
				program.end();
			break;

			default: break;
		}
	};

	program.mouse_down = [](float2 position, auto)
	{
		for(int i = 0; i < point::count; ++i)
			if(is_near(points[i], position))
				dragged_point = &points[i];
	};

	program.mouse_up = [](auto, auto)
	{
		dragged_point = nullptr;
	};

	program.mouse_move = [](auto, float2 motion)
	{
		if(dragged_point)
			(*dragged_point) += motion;
	};

	program.draw_loop = [](auto frame, auto)
	{
		points[point::radius].y() = 20;
		points[point::cord_length].y() = 40;

		// a cord can't be bigger than the diameter, except we also nudge it
		// just below diameter to avoid a bad edge case in angle representations
		points[point::cord_length].clamp(float2::zero(), points[point::radius]*2 - 0.0001f);

		frame.begin_sketch()
			.rectangle(rect{ frame.size })
			.fill(0xffffff_rgb)
		;

		{ auto sketch = frame.begin_sketch();
			for(int i = 0; i < point::count; ++i)
				sketch.ellipse(rect{float2::one(corner_radius), points[i], half});
			sketch.line_width(1).outline(0x555555_rgb);
		}

		auto r = points[point::radius].x();

		frame.begin_sketch()
			.ellipse(rect{float2::one(r*2), frame.size/2, half})
			.line_width(2).outline(0xaaaaaa_rgb)
		;

		auto begin = frame.size/2 + float2::i(r);

		frame.begin_sketch()
			.ellipse(rect{float2::one(6), begin, half})
			.line_width(2).outline(0x0000aa_rgb)
		;

		auto l = points[point::cord_length].x();

		// given a circle of radius r and a cord length l, we want to find the
		// angle which spans a cord of that length. We fix one end of the cord
		// at the point (r,0), and try to find the other end. The angle is then
		// trivially derived in desired form, be it radians of a bisector for
		// reflections
		//
		// 4 unknowns:
		// angle.x, angle.y - vector from circle center to the other end of the cord, which represents the desired angle
		// cord.x, cord.y - the components of the cord vector, from (r,0) the other end of the cord
		//
		// 4 qeuations:
		// angle vector lies on the circle
		// angle.x^2 + angle.y^2 = r^2
		//
		// the length of the cord vector is given
		// cord.x^2 + cord.y^2 = l^2
		//
		// vector (r,0) + cord vector = angle vector
		// angle.x = cord.x + r
		// angle.y = cord.y
		//
		// solving for angle.x and angle.y we get the following formula:
		auto angle = // common::cord_length_angle(r,l); // common/math.hpp
			float2{
				r - l*l/(2*r),
				l * support::root2(1 - l*l/(4*r*r))
			};
		// note the expression in the square root will be negative if l > 2*r

		frame.begin_sketch()
			.line(frame.size/2, frame.size/2 + angle)
			.line_width(2).outline(0x00aa00_rgb)
		;

		frame.begin_sketch()
			.line(begin, frame.size/2 + angle)
			.line_width(2).outline(0xaa0000_rgb)
		;

		// we check our result deriving angle values and using it to rotate a couple of points

		auto rad_lenght = r + 10;
		auto rad = std::atan(angle.y()/angle.x());
		if(rad < 0)
			rad += tau/2;
		auto rad_point = float2{std::cos(rad), std::sin(rad)} * rad_lenght;

		frame.begin_sketch()
			.ellipse(rect{float2::one(6), frame.size/2 + rad_point, half})
			.line_width(2).outline(0xaaaa00_rgb)
		;

		auto half_angle = support::halfway(float2::i(r), angle);
		// see no_trig_rotation.cpp
		auto reflect_point = common::rotate(float2::i(r - 10), half_angle);

		frame.begin_sketch()
			.ellipse(rect{float2::one(6), frame.size/2 + reflect_point, half})
			.line_width(2).outline(0xaa00aa_rgb)
		;

	};
}

bool is_near(float2 corner, float2 position)
{
	return (corner - position).magnitude() < corner_radius * corner_radius;
}
